from abc import ABC, abstractmethod
from typing import List, Dict, Any, Tuple, Optional
import os
import aiohttp
import asyncio
from pathlib import Path

class VideoInfo:
    def __init__(self, title: str, cover_url: str, formats: List[Dict[str, Any]]):
        self.title = title
        self.cover_url = cover_url
        self.formats = formats  # 包含不同清晰度选项的列表

class BaseDownloader(ABC):
    """所有下载器的基类"""
    
    def __init__(self):
        self.download_dir = os.path.join(os.path.expanduser("~"), "Downloads", "VideoDownloader")
        os.makedirs(self.download_dir, exist_ok=True)
    
    @abstractmethod
    async def extract_video_info(self, url: str) -> Optional[VideoInfo]:
        """从URL中提取视频信息"""
        pass
    
    @abstractmethod
    async def download_video(self, url: str, format_id: str, progress_callback=None) -> Tuple[bool, str]:
        """下载视频
        
        Args:
            url: 视频URL
            format_id: 格式ID
            progress_callback: 进度回调函数
            
        Returns:
            (成功与否, 文件路径或错误信息)
        """
        pass
    
    async def download_file(self, url: str, filepath: str, progress_callback=None) -> Tuple[bool, str]:
        """通用文件下载方法"""
        try:
            async with aiohttp.ClientSession() as session:
                async with session.get(url) as response:
                    if response.status != 200:
                        return False, f"下载失败，HTTP状态码: {response.status}"
                    
                    total_size = int(response.headers.get('content-length', 0))
                    downloaded = 0
                    
                    with open(filepath, 'wb') as f:
                        async for chunk in response.content.iter_chunked(1024 * 1024):  # 1MB chunks
                            f.write(chunk)
                            downloaded += len(chunk)
                            if progress_callback and total_size:
                                progress_callback(downloaded / total_size)
                    
                    return True, filepath
        except Exception as e:
            return False, f"下载过程中出错: {str(e)}"
    
    def sanitize_filename(self, filename: str) -> str:
        """清理文件名，移除不合法字符"""
        # 替换Windows和Unix系统中不允许的文件名字符
        illegal_chars = ['/', '\\', ':', '*', '?', '"', '<', '>', '|']
        for char in illegal_chars:
            filename = filename.replace(char, '_')
        return filename 